package codegen

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"gitee.com/hxchjm/goge/codegen/utils"
	"gitee.com/hxchjm/goge/codegen/utils/pathx"

	"github.com/urfave/cli"
)

const tmpFile = "%s-%d"

// CreateCommand GoCommand gen go project files from command line
func CreateCommand(c *cli.Context) error {
	dir := c.String("dir")
	namingStyle := c.String("style")
	projName := c.Args().Get(0)
	if projName == "" {
		return fmt.Errorf("no project name")
	}
	if !isSnakeStyle(projName) {
		return fmt.Errorf("name should be a snake style")
	}
	apiFile := c.String("api")
	if apiFile == "" {
		apiFile = projName + ".proto"
	}
	rootDir := filepath.Join(dir, projName)
	return DoGenProject(apiFile, rootDir, namingStyle)
}

func isSnakeStyle(str string) bool {
	for _, v := range str {
		if (v < 'a' || v > 'z') && v != '_' {
			return false
		}
	}
	return true
}

// DoGenProject gen go project files with api file
func DoGenProject(apiFile, rootDir, style string) error {
	if err := pathx.MkdirIfNotExist(rootDir); err != nil {
		return err
	}
	rootPkg, err := utils.GetParentPackage(rootDir)
	if err != nil {
		return err
	}

	if err = genMain(rootDir, rootPkg, nil, "main.go"); err != nil {
		return err
	}
	if err = genDao(rootDir, rootPkg, nil, "dao.go"); err != nil {
		return err
	}
	if err = genDB(rootDir, rootPkg, nil, "db.go"); err != nil {
		return err
	}
	if err = genRedis(rootDir, rootPkg, nil, "redis.go"); err != nil {
		return err
	}
	if err = genApp(rootDir, rootPkg, nil, "app.go"); err != nil {
		return err
	}
	if err = genModel(rootDir, rootPkg, nil, "model.go"); err != nil {
		return err
	}
	if err = genGrpc(rootDir, rootPkg, nil, "server.go"); err != nil {
		return err
	}
	if err = genHttp(rootDir, rootPkg, nil, "server.go"); err != nil {
		return err
	}
	if err = genService(rootDir, rootPkg, nil, "service.go"); err != nil {
		return err
	}
	if err = genApi(rootDir, rootPkg, nil, apiFile); err != nil {
		return err
	}
	if err = genClient(rootDir, rootPkg, nil, "client.go"); err != nil {
		return err
	}
	if err = genConfig(rootDir, rootPkg, nil, "config.yaml"); err != nil {
		return err
	}
	if err = genDockerfile(rootDir, rootPkg, nil, "Dockerfile"); err != nil {
		return err
	}
	if err = genMod(rootDir, rootPkg, nil, "go.mod"); err != nil {
		return err
	}
	return nil
}

func ProtoCommand(c *cli.Context) error {
	var err error
	apiFile := c.Args().Get(0)
	if apiFile == "" || !strings.HasSuffix(apiFile, ".proto") {
		return fmt.Errorf("no proto file")
	}
	dir := c.String("dir") //生成文件的目的目录
	if dir == "" {
		dir = "."
	}
	includeDirs := c.StringSlice("path")

	if !utils.IsFileExist(dir) {
		_ = os.MkdirAll(dir, 0777)
	}
	if err = checkProtoc(); err != nil {
		return err
	}
	if err = checkGoFast(); err != nil {
		return err
	}
	if err = checkBm(); err != nil {
		return err
	}
	if err = genPb(dir, apiFile, includeDirs); err != nil {
		return err
	}
	if err = genBm(dir, apiFile, includeDirs); err != nil {
		return err
	}
	return nil
}

/*
func backupAndSweep(apiFile string) error {
	var err error
	var wg sync.WaitGroup

	wg.Add(2)
	_ = os.MkdirAll(tmpDir, os.ModePerm)

	go func() {
		_, fileName := filepath.Split(apiFile)
		_, e := apiutil.Copy(apiFile, fmt.Sprintf(path.Join(tmpDir, tmpFile), fileName, time.Now().Unix()))
		if e != nil {
			err = e
		}
		wg.Done()
	}()
	go func() {
		if e := sweep(); e != nil {
			err = e
		}
		wg.Done()
	}()
	wg.Wait()

	return err
}

func sweep() error {
	keepTime := time.Now().AddDate(0, 0, -7)
	return filepath.Walk(tmpDir, func(fpath string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}

		pos := strings.LastIndexByte(info.Name(), '-')
		if pos > 0 {
			timestamp := info.Name()[pos+1:]
			seconds, err := strconv.ParseInt(timestamp, 10, 64)
			if err != nil {
				// print error and ignore
				fmt.Println(aurora.Red(fmt.Sprintf("sweep ignored file: %s", fpath)))
				return nil
			}

			tm := time.Unix(seconds, 0)
			if tm.Before(keepTime) {
				if err := os.Remove(fpath); err != nil {
					fmt.Println(aurora.Red(fmt.Sprintf("failed to remove file: %s", fpath)))
					return err
				}
			}
		}

		return nil
	})
}*/
